package com.angcyo.selenium.auto

import com.angcyo.javafx.base.ex.onBack
import com.angcyo.library.ex.isFileExist
import com.angcyo.log.L
import com.angcyo.selenium.DriverWebElement
import com.angcyo.selenium.DslSelenium
import com.angcyo.selenium.auto.action.*
import com.angcyo.selenium.auto.parse.AutoParse
import com.angcyo.selenium.auto.parse.HandleResult
import com.angcyo.selenium.auto.parse.toLog
import com.angcyo.selenium.bean.ActionBean
import com.angcyo.selenium.bean.TaskBean
import com.angcyo.selenium.js.exeJs
import com.angcyo.selenium.toPx
import javafx.beans.property.IntegerPropertyBase
import javafx.beans.property.ObjectPropertyBase
import javafx.beans.property.SimpleObjectProperty
import org.openqa.selenium.NoSuchWindowException
import org.openqa.selenium.PageLoadStrategy
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.edge.EdgeDriver
import org.openqa.selenium.edge.EdgeOptions
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.firefox.FirefoxOptions
import org.openqa.selenium.opera.OperaDriver
import org.openqa.selenium.opera.OperaOptions
import org.openqa.selenium.remote.AbstractDriverOptions
import org.openqa.selenium.remote.ErrorHandler
import org.openqa.selenium.remote.RemoteWebDriver
import org.openqa.selenium.remote.Response
import org.openqa.selenium.safari.SafariDriver
import org.openqa.selenium.safari.SafariOptions

/** 控制[org.openqa.selenium.WebElement]
 * Email:angcyo@126.com
 * @author angcyo
 * @date 2020/12/24
 */
open class BaseControl {

    /**驱动属性, 支持监听*/
    val driverProperty: ObjectPropertyBase<WebDriver?> = SimpleObjectProperty<WebDriver?>()

    val driver: WebDriver?
        get() = driverProperty.get()

    /**提示输出*/
    var tipAction: ((ControlTip) -> Unit)? = {
        L.i(it.title, it.des ?: "")
    }

    /**日志输出*/
    var logAction: ((String) -> Unit)? = {
        L.i(it)
    }

    /**当前运行的任务*/
    var _currentTaskBean: TaskBean? = null

    /**[ActionBean]执行器*/
    var actionSchedule: ActionSchedule = ActionSchedule(this)

    /**保存[ActionBean]执行的结果.
     * key = id
     * value = 成功or失败*/
    val actionResultMap = hashMapOf<Long, Boolean>()

    /**控制器的状态*/
    var _controlState: IntegerPropertyBase = object : IntegerPropertyBase(CONTROL_STATE_NORMAL) {

        override fun set(newValue: Int) {
            super.set(newValue)
        }

        override fun getBean(): Any {
            return this@BaseControl
        }

        override fun getName(): String {
            return "_controlState"
        }
    }

    /**任务执行完毕的原因*/
    var finishReason: String? = null

    val _autoParse = AutoParse(this)

    //具体的执行操作
    val registerActionList = mutableListOf<BaseAction>()

    /**事件监听*/
    val actionListenerList = mutableListOf<ActionListener>()

    companion object {
        const val DRIVER_EDGE = "edge"
        const val DRIVER_FIREFOX = "firefox"
        const val DRIVER_SAFARI = "safari"
        const val DRIVER_OPERA = "opera"
        const val DRIVER_CHROME = "chrome"

        const val CONTROL_STATE_NORMAL = 0
        const val CONTROL_STATE_RUNNING = 1
        const val CONTROL_STATE_FINISH = 2
        const val CONTROL_STATE_ERROR = 3
        const val CONTROL_STATE_PAUSE = 9
        const val CONTROL_STATE_STOP = 10
    }

    init {
        registerActionList.add(BackAction())
        registerActionList.add(ClickAction())
        registerActionList.add(CodeAction())
        registerActionList.add(ClearAction())
        registerActionList.add(ForwardAction())
        registerActionList.add(GetAttrAction())
        registerActionList.add(GetCssAction())
        registerActionList.add(GetTextAction())
        registerActionList.add(InputAction())
        registerActionList.add(RefreshAction())
        registerActionList.add(ToAction())
        registerActionList.add(RemoveAttrAction())
        registerActionList.add(SetAttrAction())
        registerActionList.add(ScreenshotAction())
        registerActionList.add(JumpAction())
        registerActionList.add(EnableAction())
        registerActionList.add(ErrorAction())
        registerActionList.add(SleepAction())
        registerActionList.add(PutCodeAction())
        registerActionList.add(PutTextAction())
        registerActionList.add(ConfirmAction())
        registerActionList.add(FinishAction())
        registerActionList.add(StopAction())
    }

    //初始化驱动
    fun _initDriver(driverType: String? = null, path: String? = null) {
        if (driver == null) {
            logAction?.invoke("开始连接驱动[$driverType][$path][${_currentTaskBean?.config ?: ""}]")
            if (path.isFileExist()) {
                DslSelenium.initDriver(path)
            }

            //驱动程序配置
            val _driver = when (driverType) {
                DRIVER_CHROME -> {
                    val options = ChromeOptions()
                    _initOptions(options)
                    ChromeDriver(options)
                }
                DRIVER_FIREFOX -> {
                    val options = FirefoxOptions()
                    _initOptions(options)
                    FirefoxDriver(options)
                }
                DRIVER_SAFARI -> {
                    val options = SafariOptions()
                    _initOptions(options)
                    SafariDriver(options)
                }
                DRIVER_OPERA -> {
                    val options = OperaOptions()
                    _initOptions(options)
                    OperaDriver(options)
                }
                //DRIVER_EDGE ->
                else -> {
                    val options = EdgeOptions()
                    _initOptions(options)
                    EdgeDriver(options)
                }
            }

            //error handler
            _driver.apply {
                errorHandler = object : ErrorHandler() {
                    override fun throwIfResponseFailed(response: Response?, duration: Long): Response? {
                        return try {
                            super.throwIfResponseFailed(response, duration)
                        } catch (e: Exception) {
                            L.e("异常:$e")
                            e.printStackTrace()
                            logAction?.invoke("驱动异常:$e")

                            if (e is NoSuchWindowException) {
                                driverProperty.set(null)
                                error("驱动中断."/*"${e.message}"*/)
                            }

                            null
                        }
                    }
                }

                driverProperty.set(this)

                logAction?.invoke("驱动已连接:$driverType")
            }
        }
    }

    fun _initOptions(options: AbstractDriverOptions<*>) {
        _currentTaskBean?.config?.pageLoadStrategy?.let {
            PageLoadStrategy.fromString(it)?.let {
                options.setPageLoadStrategy(it)
            }
        }
    }

    /**开始任务*/
    open fun startInner(task: TaskBean) {
        _currentTaskBean = task

        actionListenerList.forEach {
            it.onControlStart(task)
        }
    }

    //正在运行的[ActionBean]
    var _runActionBean: ActionBean? = null

    /**执行动作, [actionBean]无法处理时, 则交给[otherActionList]处理*/
    open fun runAction(
        actionBean: ActionBean,
        otherActionList: List<ActionBean>? = null,
        isPrimaryAction: Boolean = false
    ): HandleResult? {
        _runActionBean = actionBean

        //回调
        actionListenerList.forEach {
            it.onActionRunBefore(actionBean, isPrimaryAction)
        }

        var handleActionResult: HandleResult? = null

        //config
        AutoParse.configWindow(driver, actionBean.config)

        val handleList = actionBean.check?.handle
        val eventList = actionBean.check?.event

        if (actionBean.check == null) {
            //未指定check, 直接操作浏览器Driver
            handleActionResult = _autoParse.handle(this, actionBean, handleList)
        } else {
            val eventElementList = if (eventList == null)
                listOf(DriverWebElement(_autoParse.getBounds())) else _autoParse.parseSelector(this, eventList)

            if (eventElementList.isEmpty()) {
                logAction?.invoke("[event]未匹配到元素:${eventList}")
                //未找到元素
                val handleResult = _autoParse.handle(this, actionBean, actionBean.check?.other)
                if (!handleResult.success) {
                    //还是未成功
                    otherActionList?.forEach {
                        actionSchedule.scheduleAction(it)
                    }
                }
            } else {
                //找到了目标元素
                logAction?.invoke(eventElementList.toLog("[event]匹配到元素(${eventElementList.size})${actionBean.check}↓"))
                val result = _autoParse.handle(this, actionBean, handleList, eventElementList)
                handleActionResult = result
                if (result.success) {
                    //未处理成功
                    actionBean.check?.success?.let {
                        handleActionResult = _autoParse.handle(this, actionBean, it, eventElementList)
                    }
                } else {
                    //未处理成功
                    actionBean.check?.fail?.let {
                        handleActionResult = _autoParse.handle(this, actionBean, it, eventElementList)
                    }
                }
            }
        }

        //退出框架
        eventList?.forEach {
            if (it.frame?._frame != null) {
                logAction?.invoke("退出[event]iframe:${it.frame}")
                it.frame?._frame = null
                driver?.switchTo()?.defaultContent()
            }
        }

        if (isPrimaryAction) {
            //保存执行结果.
            actionResultMap[actionBean.actionId] = handleActionResult?.success ?: false

            //处理结果
            handleActionResult?.apply {
                if (success) {
                    //处理成功
                    //showElementTip(elementList)
                    actionSchedule.next(actionBean)
                } else {
                    //未处理成功
                    if (actionSchedule._nextTime <= 0) {
                        //防止ui卡死
                        actionSchedule._nextTime = 160
                    }
                }
            }
        }

        //回调
        actionListenerList.forEach {
            it.onActionRunAfter(actionBean, isPrimaryAction, handleActionResult)
        }

        _runActionBean = null

        return handleActionResult
    }

    /**停止控制器, 退出线程*/
    open fun stop(reason: String) {

    }

    /**运行结束*/
    open fun finish(reason: String = "正常完成!") {
        _end(reason)
    }

    /**运行异常结束*/
    open fun error(reason: String = "异常完成!") {
        _end(reason)
    }

    fun _end(reason: String) {
        actionSchedule.scheduleEnd()
        finishReason = reason
        val tip = ControlTip().apply {
            title = buildString {
                append(_currentTaskBean?.title)
                append(" ")
                append(actionSchedule.indexTip())
                append(" ")
                append(reason)
                append(" ")
                append(_controlState.get().controlStateToStr())
            }
            des = "$finishReason 耗时:${actionSchedule.durationStr()}"
        }
        tipAction?.invoke(tip)
        logAction?.invoke("${tip.title} ${tip.des}")

        _currentTaskBean?.apply {
            actionListenerList.forEach {
                it.onControlEnd(this, _controlState.get(), reason)
            }
        }
    }

    /**调用js, 显示选择的元素提示*/
    fun showElementTip(list: List<WebElement>?) {
        onBack(false) {
            (driver as? RemoteWebDriver)?.let { web ->
                //先移除所有之前的提示
                web.exeJs("remove_all_tip.js")
                //再添加新的提示
                list?.forEach {
                    try {
                        if (it.isValidElement()) {
                            val rect = it.rect
                            web.exeJs(
                                "append_tip.js",
                                rect.x.toPx(),
                                rect.y.toPx(),
                                rect.width.toPx(),
                                rect.height.toPx()
                            )
                        }
                    } catch (e: Exception) {
                        L.w(e)
                    }
                }
            }
        }
    }

    /**执行动作*/
    fun handleAction(actionBean: ActionBean, element: WebElement, action: String?): HandleResult {
        val result = HandleResult()
        val elementList = mutableListOf<WebElement>()

        var isActionIntercept = false
        registerActionList.forEach {
            //是否要处理指定的action
            if (it.interceptAction(this, action)) {
                isActionIntercept = true
                //运行处理
                it.runAction(this, element, actionBean, action ?: "").apply {
                    result.success = success || result.success
                    if (success) {
                        //把处理成功的元素收集起来
                        if (!elementList.containsAll(this.elementList ?: emptyList())) {
                            elementList.addAll(this.elementList!!)
                        }
                    }
                }
            }
        }
        if (!isActionIntercept) {
            //动作未识别, 或者无法处理
            result.success = true
            logAction?.invoke("无法处理[$action],跳过.")
        }
        if (result.success) {
            result.elementList = elementList
        }
        return result
    }
}

fun BaseControl.findAction(actionId: Long?): ActionBean? =
    if (actionId == null) null else _currentTaskBean?.actionList?.find { it.actionId == actionId }